#include<cstring>
#include"Socket.hpp"
#include"log.hpp"


int SIZE =  1024;  //poll数组原始大小，不足可以扩容
const int deafult_num = -1;
class PollServer{
    public:
        PollServer(uint16_t port)
            :_port(port),_listen_socket(new TcpSocket(_port))
        {}
        void Init()
        {
            _listen_socket->Build_Socket(_port);
            _fds = (struct pollfd*)malloc(sizeof(struct pollfd)*SIZE);//分配poll数组
            if(_fds == nullptr){
                LOG(log_level::FATAL)<<"malloc poll array failed";
                exit(1);
            }
            for(int i = 0;i<SIZE;i++){
                _fds[i].fd = deafult_num;
                _fds[i].events = 0;
                _fds[i].revents = 0;
            }
            //将listenfd加入到数组中
            _fds[0].fd = _listen_socket->Fd();
            _fds[0].events |= POLLIN;//监听sockfd可读
        }
        void Loop()
        {
            int timeout = -1;
            _is_running = true;
            while(_is_running){
                //accept是阻塞的，listensockfd获取新连接，本质是一种IO
                //把listensockfd注册到poll中，监听新连接(帮忙‘等’)
                int n = poll(_fds,SIZE,timeout);//timeout为-1表示阻塞，timeout为0表示不阻塞，timeout为其他值表示超时时间
                switch(n)
                {
                    case 0:
                        std::cout<<"time out"<<std::endl;
                        break;
                    case -1:
                        perror("poll error");
                        break;
                    default://内核告诉用户有fd就绪(可能是监听sockfd获取新连接，也可能是其他合法fd数据准备好了),处理事件
                        std::cout<<"new connection "<<std::endl;
                        Dispatch();
                        break;
                }
            }
            _is_running = false;
        }
        void Accept()//listensockfd获取新连接成功，不需要等（poll帮等了），直接调用accept
        {
            struct sockaddr_in client_addr;
            int new_sockfd = _listen_socket->Accept((struct sockaddr*)&client_addr);//不会阻塞
            if(new_sockfd < 0){
                return;
            }else{//新连接
                std::string client_ip = inet_ntoa(client_addr.sin_addr);//获取客户端ip
                uint16_t client_port = ntohs(client_addr.sin_port);//获取客户端端口
                LOG(log_level::INFO)<<"new connection from "<<client_ip<<":"<<client_port;
            }
            //将新连接的sockfd加入到poll数组中
            int pos = 0;
            for(pos = 0;pos<SIZE;pos++){//只是连接成功了，数据是否准备成功不清楚,即将new_sockfd加入select中，等待数据准备
                if(_fds[pos].fd == deafult_num){//找到一个空位
                    _fds[pos].fd = new_sockfd;
                    _fds[pos].events |= POLLIN;
                    break;
                }
            }
            if(pos == SIZE){//数组已满,扩容
                SIZE *= 2;
                _fds = (struct pollfd*)realloc(_fds,sizeof(struct pollfd)*(SIZE));
                if(_fds == nullptr){
                    LOG(log_level::FATAL)<<"realloc poll array failed";
                    return;
                }
                _fds[pos].fd = new_sockfd;
                _fds[pos].events |= POLLIN;
                return;
            }
        }
        void DealData(int sockfd,int pos)//处理IO数据
        {
            //只是写一个demo,数据的处理想要完备，必须引入协议，太麻烦了
            char buffer[1024];
            ssize_t n = recv(sockfd, buffer, sizeof(buffer)-1, 0);//不会阻塞
            if(n>0){
                buffer[n] = '\0';
                std::cout<<"client say: "<<buffer<<std::endl;
                send(sockfd, buffer, n, 0);//发是可能会阻塞的，因为只处理了读的逻辑，用poll,epoll写这份代码时会处理
                
            }else if(n==0){
                LOG(log_level::INFO)<<"client quit "<<_fds[pos].fd;
                close(sockfd);
                _fds[pos].fd = deafult_num;
                _fds[pos].events = _fds[pos].revents = 0;
            }else{
                LOG(log_level::ERROR)<<"recv error "<<_fds[pos].fd;
                close(sockfd);
                _fds[pos].fd = deafult_num;
                _fds[pos].events = _fds[pos].revents = 0;
            }
        }
        void Dispatch()
        {
            for(int i = 0;i<SIZE;i++){
                if(_fds[i].fd==deafult_num)//不是关注的文件描述符
                    continue;
                else if(_fds[i].fd==_listen_socket->Fd() && _fds[i].revents & POLLIN){//是监听sockfd，且有新连接里准备就绪了
                    Accept();
                }else{//其他合法文件描述符准备好数据了，进行数据IO处理
                    if(_fds[i].revents & POLLIN){//可读
                        DealData(_fds[i].fd,i);
                    }else if(_fds[i].revents & POLLOUT){
                        //可写先不实现
                    }
                }
            }
        }
        ~PollServer()
        {}
    private:
        uint16_t _port;
        std::unique_ptr<Socket> _listen_socket;
        bool _is_running = false;
        struct pollfd *_fds;//poll数组
};